package org.delphy.indy2.containers
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	import mx.containers.Canvas;
	import mx.core.Container;
	
	import org.delphy.indy2.containers.carbonPanelClasses.*;
	import org.delphy.indy2.core.ICarbonComponent;
	import org.delphy.indy2.events.CarbonPanelEvent;
	import org.delphy.indy2.layers.FilterLayer;

	//----------------------------------
    //  Events
    //----------------------------------	
    
    /**
    * Dispatched at the time when the panel is being resized (the resizer is being dragged)
    * 
    * @eventType org.delphy.indy2.events.CarbonPanelEvent.RESIZE_START
    */		
	[Event(name="resizeStart", type="org.delphy.indy2.events.CarbonPanelEvent")]
	
	/**
	 * Dispatched at the time when the panel stops resized (user release mouse)
	 * 
	 * @eventType org.delphy.indy2.events.CarbonPanelEvent.RESIZE_STOP
	 */
	 [Event(name="resizeStop", type="org.delphy.indy2.events.CarbonPanelEvent")]
	
	/**
	 * Dispatched when the mouse moves over the border
	 * 
	 * @eventType org.delphy.indy2.events.CarbonPanelEvent.RESIZER_MOUSE_OVER
	 */
	 [Event(name="resizerMouseOver", type="org.delphy.indy2.events.CarbonPanelEvent")]

	/**
	 * Dispatched when the mouse moves off the border
	 * 
	 * @eventType org.delphy.indy2.events.CarbonPanelEvent.RESIZER_MOUSE_OUT
	 */
	 [Event(name="resizerMouseOut", type="org.delphy.indy2.events.CarbonPanelEvent")]
	 
	//----------------------------------
    //  Styles
    //----------------------------------			
    
	 /**
	 * Border class
	 * Currently, we provide two built-in skins, named CarbonBorder and CarbonNullBorder. The latter simply draws nothing
	 * 
	 * @default: CarbonNullBorder
	 */
	 [Style(name="borderClass", type="Class", inherit="no")]
	 
	 /**
	 * Content class
	 * 
	 * @default: CarbonPanelContent
	 */
	 [Style(name="contentClass", type="Class", inherit="no")]

	 /**
	 * FilterLayer
	 * This is a dummy canvas for hosting filters 
	 * 
	 * @default: carbonPanelShadowFilter
	 */
	 [Style(name="filterLayer", type="String", inherit="no")]
	 
	public class CarbonPanel extends Canvas implements ICarbonComponent
	{
		//----------------------------------
    	//  Border
    	//
    	//  Note that this "border" is of type ICarbonBorder, which has nothing to do with Flex's Border classes. However, there does exists name
    	//  conflicts, so if you try to set Flex's built-in border properties, be extra careful or you can cause your program freeze. 
    	//----------------------------------		
		
		/**
		 * @protected
		 * Default border class
		 * 
		 * @default CarbonBorder
		 */
		 protected var defaultBorderClass:Class = CarbonBorder;
		
		/**
		 * @protected
		 * The border of the panel, of type ICarbonBorder
		 * 
		 * @default CarbonBorder
		 */	
		protected var _border:ICarbonBorder;
		public function get border():ICarbonBorder{
			return _border;
		}
		
		//----------------------------------
    	//  Client
    	//----------------------------------	
    	
    	/**
    	 * @protected
    	 * Default content class
    	 * 
    	 * @defualt CarbonPanelContent
    	 */
    	 protected var defaultContentClass:Class = CarbonPanelContent;	
		
		/**
		 * @protected
		 * In CarbonPanel, all children are actually added into _content, which is by default a Canvas.
		 * 
		 * @default CarbonPanelContent
		 */
		protected var _content:ICarbonPanelContent;
		public function get content():ICarbonPanelContent{
			return _content;
		}

		//----------------------------------
    	//  Outter Filters Layer
    	//----------------------------------		
		
		/**
		 * @protected
		 * 
		 * This is dirty hack, but if we simply apply a filter (say, a DropShadowFilter) directly onto our panel, we will most likely
		 * lose transparence of the client area. Thus, in order to gain both effects, we are going to apply the filters on a "fake" canvas.
		 * Such canvas is invislble, because we are going to set the DropShadowFilter.knotOut=true
		 */
		 protected var _filterLayer:FilterLayer;

		//----------------------------------
    	//  Resizer
    	//----------------------------------		
		
		/**
		 * @protected
		 * 
		 * Just a simple indicator of whether _resizerSize has been changed
		 */
		protected var resizerSizeChanged:Boolean=false;
				
		/**
		 * @public
		 * Width and height of the resizer
		 * 
		 * @default 12
		 */
		[Inspectable(type="Number", defaultValue="12")] 
		protected var _resizerSize:Number=12;
		public function get resizerSize():Number{
			return _resizerSize;
		}		
		public function set resizerSize(value:Number):void{
			if(_resizerSize!=value){
				_resizerSize=value;
				resizerSizeChanged=true;
				invalidateDisplayList();
			}
		}
		
		/**
		 * @public
		 * Resizer is an invisible object used to resize its parent panel
		 */
		protected var _resizer:Canvas;
		public function get resizer():Canvas{
			return _resizer;
		}
		
		/**
		 * @public
		 * This indicates whether the panel is in the state of being resized
		 */
		protected var _resizing:Boolean=false;
		public function get resizing():Boolean{
			return _resizing;
		}
		
		/**
		 * @public
		 * This variable indicates whether the panel is allowed to be resized
		 * 
		 * @default false
		 */
		[Inspectable(type="Boolean", defaultValue="false")]		
		protected var _resizable:Boolean=true;
		public function get resizable():Boolean{
			return _resizable;
		}
		public function set resizable(value:Boolean):void{
			// We need to add/remove event handlers
			_resizable=value;
			if(_resizable){
				addResizerListeners();
			}
			else{
				removeResizerListeners();
			}
				
		}

		//----------------------------------
		//  Methods
		//----------------------------------	

		/**
		 * Constructor
		 */				
		public function CarbonPanel()
		{
			super();
		}

		//----------------------------------
    	//  Overridden Methods
    	//----------------------------------		
		
		/**
		 *  The following functions redirect requests to _content
		 */
		override public function addChild(child:DisplayObject):DisplayObject{
			// The first time this function is called, we maybe counter the problem that at this time, _content is null.
			// So, we need to check if it is necessery to create a _content
			if(!_content)
				createContent();
				
			return Container(_content).addChild(child);
		}
		
		override public function addChildAt(child:DisplayObject, index:int):DisplayObject{
			return Container(_content).addChildAt(child,index);
		}
		
		override public function getChildAt(index:int):DisplayObject{
			return Container(_content).getChildAt(index);
		}
		
		override public function getChildByName(name:String):DisplayObject{
			return Container(_content).getChildByName(name);
		}
		
		override public function getChildIndex(child:DisplayObject):int{
			return Container(_content).getChildIndex(child);
		}
		
		override public function getChildren():Array{
			return Container(_content).getChildren();
		} 
				
		override protected function createChildren():void{
			super.createChildren();
			
			// Fall to default style if no styleName is specified
			if(!styleName)
				styleName="carbonPanel";
			
			// Create a border
			createCarbonBorder();
			
			// The filter canvas, remember we will put all filters onto it, thus it serves as some kind of background
			_filterLayer=new FilterLayer(this);
			_filterLayer.setStyle("topLeftRadius", _border.topLeftRadius);
			_filterLayer.setStyle("topRightRadius", _border.topRightRadius);
			_filterLayer.setStyle("bottomLeftRadius", _border.bottomLeftRadius);
			_filterLayer.setStyle("bottomRightRadius", _border.bottomRightRadius);
			
			// Create the content canvas
			if(!_content)
				createContent();
			
			// Create the resizer, the default size is 12
			_resizer=new Canvas();
			if(_resizable)
				addResizerListeners();
			
			// We set resizerSizeChaged=true, so that resizer can be properly placed in function updateDisplayList()
			resizerSizeChanged=true;
						
			// Add the content and the border to the rawChildren list
			// Note that the order of adding children here is important, since the "border" component is NOT transparent. 
			// If you add the border to the rawChildren first, you'll end up with a non-interactive content
			rawChildren.addChild(_filterLayer);
			rawChildren.addChild(DisplayObject(_border));
			rawChildren.addChild(DisplayObject(_content));
			rawChildren.addChild(_resizer);			
		}
		
		override public function styleChanged(styleProp:String) : void{
			super.styleChanged(styleProp);

			if(styleProp && styleProp.search("styleName")!=-1){
				// Refresh filterLayer
				if(_filterLayer){
					_filterLayer.initFilters(getStyle("filterLayer"));
					_filterLayer.invalidateDisplayList();
				}
			}
			
			if(styleProp && styleProp.search("borderClass")!=-1){
				// Remove the current border and create a new one
				if(_border)
					rawChildren.removeChild(DisplayObject(_border));
				
				createCarbonBorder();
				rawChildren.addChild(DisplayObject(_border));
			}
			
			if(styleProp && styleProp.search("contentClass")!=-1){
				// Remove the current content and create a new one
				if(_content)
					rawChildren.removeChild(DisplayObject(_content));
				
				createContent();
				rawChildren.addChild(DisplayObject(_content));
			}
		}
		
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			super.updateDisplayList(unscaledWidth,unscaledHeight);
			
			if(_border!=null){
				DisplayObject(_border).width=unscaledWidth;
				DisplayObject(_border).height=unscaledHeight;
			}

			if(resizerSizeChanged){
				resizerSizeChanged=false;
				
				resizer.width=_resizerSize;
				resizer.height=_resizerSize;
				placeResizer();
			}
			
			if(_content!=null){
				Container(_content).width=unscaledWidth - _border.weight * 2;
				Container(_content).height=unscaledHeight - _border.weight * 2;
				Container(_content).x=_border.weight;
				Container(_content).y=_border.weight;
			}			
		}
		
		/**
		 * @protected
		 * 
		 * This function simply create the content container
		 */
		protected function createContent():void{
			var contentClass:Class=getStyle("contentClass");
			contentClass = contentClass ? contentClass : defaultContentClass;
			_content=new contentClass();
		}

		/**
		 * @protected
		 * 
		 * This function simply create a ICarbonBorder instance
		 */
		protected function createCarbonBorder():void{
			// Remove the old one
			var borderClass:Class=getStyle("borderClass");
			// Fallback to CarbonNullBorder, if no borderSkin is set
			if(!borderClass)
				borderClass=defaultBorderClass;
			
			_border=new borderClass();
		}
		
		/**
		 * @public
		 * 
		 * This method cause the panel to be in the state of resizing
		 */
		public function startResize(evt:Event):void{
			_resizing=true;
			stage.addEventListener(Event.ENTER_FRAME,onResizing);
			stage.addEventListener(MouseEvent.MOUSE_UP,stopResize);
			
			_border.showHighlight();
			_border.playResizingAnimation();
			
			dispatchEvent(new CarbonPanelEvent(CarbonPanelEvent.RESIZE_START));
		}
		
		/**
		 * @public
		 * 
		 * This method stops resizing the panel
		 */
		public function stopResize(evt:Event):void{
			_resizing=false;
			stage.removeEventListener(Event.ENTER_FRAME,onResizing);
			stage.removeEventListener(MouseEvent.MOUSE_UP,stopResize);
			
			_border.showHighlight();
			_border.stopResizingAnimation();
			
			dispatchEvent(new CarbonPanelEvent(CarbonPanelEvent.RESIZE_STOP));
		}
		
		/**
		 * @protected
		 * 
		 * Event handler for MouseEvent.MOUSE_OVER
		 */
		private function onResizerMouseOver(evt:MouseEvent):void{
			_border.showHighlight();
			
			dispatchEvent(new CarbonPanelEvent(CarbonPanelEvent.RESIZER_MOUSE_OVER));
		}

		/**
		 * @protected
		 * 
		 * Event handler for MouseEvent.MOUSE_OUT
		 */		
		private function onResizerMouseOut(evt:MouseEvent):void{
			if(!resizing)
				_border.hideHighlight();
				
			dispatchEvent(new CarbonPanelEvent(CarbonPanelEvent.RESIZER_MOUSE_OUT));	
		}

		/**
		 * @protected
		 * 
		 * Event handler for Event.ENTER_FRAME, only appliable when resizing the border
		 */
		
		protected function onResizing(evt:Event):void{
			var topLeftPoint:Point=new Point(x,y);
			topLeftPoint=parent.localToGlobal(topLeftPoint);
			
			var newWidth:Number=stage.mouseX-topLeftPoint.x;
			var newHeight:Number=stage.mouseY-topLeftPoint.y;
			newWidth= (newWidth > 0) ? newWidth : 0;
			newHeight= (newHeight > 0) ? newHeight : 0;
			
			width=newWidth;
			height=newHeight;
			
			placeResizer();			
		}
		
		/**
		 * @protected
		 * Place the resizer at the bottom-right of the panel
		 */
		protected function placeResizer():void{
			_resizer.x=width-_resizerSize;
			_resizer.y=height-_resizerSize;
		}		
		
		/**
		 * @protected
		 * Register/unregister event handlers for resizer
		 */
		protected function addResizerListeners():void{
			if(_resizer!=null){
				_resizer.addEventListener(MouseEvent.MOUSE_DOWN,startResize);
				_resizer.addEventListener(MouseEvent.MOUSE_OVER,onResizerMouseOver);
				_resizer.addEventListener(MouseEvent.MOUSE_OUT,onResizerMouseOut);				
			}
		}
		
		protected function removeResizerListeners():void{
			if(_resizer!=null){
				_resizer.removeEventListener(MouseEvent.MOUSE_DOWN,startResize);
				_resizer.removeEventListener(MouseEvent.MOUSE_OVER,onResizerMouseOver);
				_resizer.removeEventListener(MouseEvent.MOUSE_OUT,onResizerMouseOut);				
			}			
		}
		
		/**
		 * @public
		 */
		public function get defaultStyleName():String{
			return "carbonPanel";
		}
	}
}